﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.ComponentModel;
using CollectionExtensions;
using System.Collections.Generic;

namespace CollectionExtensionsTests
{
    [TestClass]
    public class SortingObservableCollectionTests
    {
        [TestMethod]
        public void Creation()
        {
            var c = new SortingObservableCollection<TestObj, int>(Comparer<int>.Default, o => o.Value);
            Assert.AreEqual<int>(0, c.Count);
            c.Add(new TestObj("name", 1));
            Assert.AreEqual<int>(1, c.Count);

            Assert.AreEqual<string>("name", c[0].Name);
            Assert.AreEqual<int>(1, c[0].Value);
        }

        #region Make sure they can't disrupt order

        [TestMethod]
        [ExpectedException(typeof(NotSupportedException))]
        public void TrySetIndex()
        {
            var c = new SortingObservableCollection<TestObj, int>(Comparer<int>.Default, o => o.Value);
            c[0] = new TestObj("name", 1);
        }

        [TestMethod]
        [ExpectedException(typeof(NotSupportedException))]
        public void TryInsert()
        {
            var c = new SortingObservableCollection<TestObj, int>(Comparer<int>.Default, o => o.Value);
            c.Insert(0, new TestObj("name", 1));
        }

        [TestMethod]
        [ExpectedException(typeof(NotSupportedException))]
        public void TryMove()
        {
            var c = new SortingObservableCollection<TestObj, int>(Comparer<int>.Default, o => o.Value);
            c.Add(new TestObj("name", 1));
            c.Add(new TestObj("other", 2));

            c.Move(1, 0);
        }

        #endregion

        #region Simple sorts

        [TestMethod]
        public void SimpleFourItemSort()
        {
            var c = new SortingObservableCollection<TestObj, int>(Comparer<int>.Default, o => o.Value);

            var one = new TestObj("first", 1);
            var two = new TestObj("second", 2);
            var three = new TestObj("third", 3);
            var four = new TestObj("fourth", 4);

            c.Add(one);
            c.Add(two);
            Assert.AreEqual<TestObj>(one, c[0]);
            Assert.AreEqual<TestObj>(two, c[1]);

            c.Add(four);
            c.Add(three);
            Assert.AreEqual<TestObj>(four, c[3]);
            Assert.AreEqual<TestObj>(three, c[2]);

            c.Clear();
            Assert.AreEqual<int>(0, c.Count);

            c.Add(four);
            c.Add(three);
            c.Add(two);
            c.Add(one);
            Assert.AreEqual<TestObj>(one, c[0]);
            Assert.AreEqual<TestObj>(two, c[1]);
            Assert.AreEqual<TestObj>(three, c[2]);
            Assert.AreEqual<TestObj>(four, c[3]);
        }

        [TestMethod]
        public void SimpleFourItemSortDynamicUpdate()
        {
            var c = new SortingObservableCollection<TestObj, int>(Comparer<int>.Default, o => o.Value);

            var one = new TestObj("first", 1);
            var two = new TestObj("second", 2);
            var three = new TestObj("third", 3);
            var four = new TestObj("fourth", 4);

            c.Add(one);
            c.Add(two);
            c.Add(three);
            c.Add(four);

            two.Value = 5;
            Assert.AreEqual<TestObj>(two, c[3]);

            c.Remove(two);
            Assert.AreEqual<int>(3, c.Count);

            // Test to see if the collection reacts to changing a non-existant item
            two.Value = 2;
        }

        [TestMethod]
        public void SimpleFourItemAddRemove()
        {
            var c = new SortingObservableCollection<TestObj, int>(Comparer<int>.Default, o => o.Value);

            var one = new TestObj("first", 1);
            var two = new TestObj("second", 2);
            var three = new TestObj("third", 3);
            var four = new TestObj("fourth", 4);

            c.Add(one);
            c.Add(two);
            c.Add(three);
            c.Add(four);

            c.Remove(two);
            two.Value = 5;
            c.Add(two);
            Assert.AreEqual<TestObj>(two, c[3]);

            var fakeTwo = new TestObj("fakeSecond", 2);
            c.Add(fakeTwo);
            Assert.AreEqual<TestObj>(fakeTwo, c[1]);
            Assert.AreEqual<TestObj>(two, c[4]);

            fakeTwo.Value = 5;
            Assert.AreEqual<TestObj>(fakeTwo, c[4]);
            Assert.AreEqual<TestObj>(two, c[3]);

            one.Value = 5;
            Assert.AreEqual<TestObj>(one, c[4]);

            one.Value = 3;
            Assert.AreEqual<TestObj>(one, c[1]);
        }

        #endregion
    }

    public class TestObj : INotifyPropertyChanged
    {
        private int val;

        public int Value
        {
            get { return val; }
            set
            {
                if (val != value)
                {
                    val = value;
                    NotifyPropertyChanged("Value");
                }
            }
        }

        public string Name { get; set; }

        public TestObj(string name, int value)
        {
            Name = name;
            val = value;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
